# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import datetime
import functools
import typing

from coin.exchange.bitflyer_v1.kr_rest.constants import (
    coin_list,
    fiat_list,
    expiration_list,
)
from coin.exchange.bitflyer_v1.kr_rest.currency import (BitflyerCurrency, to_native_currency)
from coin.exchange.bitflyer_v1.kr_rest.product_util import (
    gen_bitflyer_product_list,)
from coin.exchange.base.kr_rest.futures_product import (
    NativeFuturesProduct,
    NormFuturesProduct,
    parse_futures_product_symbol,
)
from coin.exchange.bitflyer_v1.kr_rest.product_util import (
    gen_bitflyer_native_symbol,
    parse_bitflyer_native_symbol,
)
import coin.exchange.base.kr_rest.derivative_util as derivative_util


class BitflyerFuturesProduct(NativeFuturesProduct):
  _exchange = 'Bitflyer'
  NativeCurrencyType = BitflyerCurrency

  def __init__(self,
               base: BitflyerCurrency,
               quote: BitflyerCurrency,
               expiry: typing.Union[datetime.datetime, datetime.date, str],
               native_symbol):

    if isinstance(expiry, str) and expiry == 'FX':
      expiry = 'PERPETUAL'

    expiry = derivative_util.to_expiry_datetime(expiry)
    expiry_str = derivative_util.to_expiry_str(expiry)
    assert expiry_str in expiration_list, expiry_str

    assert type(native_symbol) == str

    super().__init__(base=base, quote=quote, expiry=expiry, native_symbol=native_symbol)

  @classmethod
  def FromProductImpl(cls, product, current_datetime):
    assert isinstance(product, NormFuturesProduct), type(product)
    expiry_str = derivative_util.to_expiry_str(product.expiry)
    return cls(BitflyerCurrency.FromCurrency(product.base, current_datetime),
               BitflyerCurrency.FromCurrency(product.quote, current_datetime),
               product.expiry,
               gen_bitflyer_native_symbol(product.base, product.quote, expiry_str))

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromStrProductImpl(cls, product, current_datetime):
    # This if is added to support Spot-like behavior.
    if product == "BTC-JPY" or product == "BTC_JPY":
      product = "BTC-JPY.IMMEDIATE"

    parsed_result = parse_futures_product_symbol(product)
    base = BitflyerCurrency.FromStr(parsed_result['base'], current_datetime)
    quote = BitflyerCurrency.FromStr(parsed_result['quote'], current_datetime)
    native_symbol = gen_bitflyer_native_symbol(base, quote, parsed_result['expiry'])
    assert to_native_currency(base.symbol, current_datetime) in coin_list, base
    assert to_native_currency(quote.symbol, current_datetime) in \
           fiat_list + ['BTC'], quote
    assert parsed_result['expiry'] in expiration_list
    return cls(base, quote, parsed_result['expiry'], native_symbol)

  @classmethod
  @functools.lru_cache(maxsize=256)
  def FromStrNativeProductImpl(cls, product: str, current_datetime):
    parsed_result = parse_bitflyer_native_symbol(product, current_datetime)
    base = BitflyerCurrency.FromStr(parsed_result['base'], current_datetime)
    quote = BitflyerCurrency.FromStr(parsed_result['quote'], current_datetime)
    assert to_native_currency(base.symbol, current_datetime) in coin_list, base
    assert to_native_currency(quote.symbol, current_datetime) in \
           fiat_list + ['BTC'], quote
    assert parsed_result['expiry'] in expiration_list
    return cls(base, quote, parsed_result['expiry'], product)


def get_bitflyer_product_list():
  product_list = []
  result = gen_bitflyer_product_list()
  for res in result:
    try:
      product = BitflyerFuturesProduct.FromStrNativeProduct(res['native_symbol'])
      product_list.append(product)
    except Exception as e:
      print('Fail to generate Bitflyer futures product.', res, type(e), e)
      continue
  return product_list
